package com.tomicalab.mobiletoken.ckca.utils;

import iaik.asn1.ObjectID;
import iaik.asn1.structures.Name;
import iaik.pkcs.pkcs11.Module;
import iaik.pkcs.pkcs11.Session;
import iaik.pkcs.pkcs11.Slot;
import iaik.pkcs.pkcs11.Token;
import iaik.pkcs.pkcs11.TokenException;
import iaik.pkcs.pkcs11.objects.DHPrivateKey;
import iaik.pkcs.pkcs11.objects.DSAPrivateKey;
import iaik.pkcs.pkcs11.objects.Key;
import iaik.pkcs.pkcs11.objects.RSAPrivateKey;
import iaik.pkcs.pkcs11.objects.X509PublicKeyCertificate;
import iaik.pkcs.pkcs11.wrapper.CK_ATTRIBUTE;
import iaik.pkcs.pkcs11.wrapper.CK_MECHANISM;
import iaik.pkcs.pkcs11.wrapper.CK_TOKEN_INFO;
import iaik.pkcs.pkcs11.wrapper.PKCS11;
import iaik.pkcs.pkcs11.wrapper.PKCS11Connector;
import iaik.pkcs.pkcs11.wrapper.PKCS11Constants;
import iaik.pkcs.pkcs11.wrapper.PKCS11Exception;
import iaik.security.provider.IAIK;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAParams;
import java.util.Collection;
import iaik.pkcs.pkcs11.objects.Object;
import javax.crypto.spec.DHParameterSpec;

import com.ft.key.audio.comm.ACException;

import android.content.Context;
import android.util.Log;
import cfca.mobile.keydevice.AudioToken;

public class P11Other {
	final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
	public static int isLoadlib=0;
	public static int isTokenPlugned=0;
	public static PKCS11 myPKCS11Module_;
	public static long token_ = -1L;
	public static long session_;
	
	protected static Module _module_;
	protected static Slot[] _slots_;
	protected static Token _token_;
	protected static Session _session_;
	
	private static CK_TOKEN_INFO tokenInfo;
	
	private static int MAX_OBJS = 100;
	
	
	public static AudioToken audioToken;
	public static boolean p11initialize(Context _context) {
		try{
			if(audioToken==null)
			{
				if(_context!=null)
					audioToken = new AudioToken(_context);
			}
			
			if(_context!=null)
			{
				File _cache = _context.getFilesDir();
				if(_cache!=null)
				{
					String _path=_cache.getAbsolutePath();
					boolean canWrite = _cache.canWrite();
					boolean canRead = _cache.canRead();
					
				}
			}
				
		}
		catch (ACException e1) {
			e1.printStackTrace();
			return false;
		}
		
		try {
			if(isLoadlib==0)
			{
				System.load("/data/data/com.example.vnpt/lib/libtomikey-2003a_api.so");
				System.load("/data/data/com.tomicalab.mobiletoken.ckca/lib/libtomikey-2003a.so");
				System.load("/data/data/com.tomicalab.mobiletoken.ckca/lib/libtomikey-2003ajni.so");
				myPKCS11Module_ = PKCS11Connector.connectToPKCS11Module(
						"/data/data/com.tomicalab.mobiletoken.ckca/lib/libtomikey-2003a.so",
						"/data/data/com.tomicalab.mobiletoken.ckca/lib/libtomikey-2003a.so");
				isLoadlib = 1;
			}
			
		} catch (IOException e1) {
			e1.printStackTrace();
			return false;
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			myPKCS11Module_.C_Initialize(null);

		} catch (PKCS11Exception e) {
			if (e.getErrorCode() == PKCS11Constants.CKR_CRYPTOKI_ALREADY_INITIALIZED) {
				return true;
			}
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public static void getInfo() throws PKCS11Exception {
		myPKCS11Module_.C_GetInfo();
	}
	
	public static void getSlotInfo() throws PKCS11Exception {
		long[] slotIDs = myPKCS11Module_.C_GetSlotList(true);
		
		if(slotIDs.length!=0 && slotIDs[0]!=0)
			myPKCS11Module_.C_GetSlotInfo(slotIDs[0]);
			
	}

	public static void getTokenInfo() throws PKCS11Exception {
		/*
		long[] tokenIDs = myPKCS11Module_.C_GetSlotList(true);
		if(tokenIDs == null)
			Log.e(Constant.Debug, "tokenID: IDNULL");
		if(tokenIDs.length!=0 && tokenIDs[0]!=0)
		{
			tokenInfo = myPKCS11Module_.C_GetTokenInfo(tokenIDs[0]);
			token_ = tokenIDs[0];
		}
		*/
		long[] tokenIDs = myPKCS11Module_.C_GetSlotList(true);
		for (int i = 0; i < tokenIDs.length; i++) {
			tokenInfo = myPKCS11Module_.C_GetTokenInfo(tokenIDs[i]);
			if (token_ == -1L) {
				token_ = tokenIDs[i];
			}
		}
	}

	public static void openSession() throws PKCS11Exception {
		session_ = myPKCS11Module_.C_OpenSession(token_,
				PKCS11Constants.CKF_SERIAL_SESSION
						| PKCS11Constants.CKF_RW_SESSION, null, null);
	}

	public static int loginUser(String password) throws PKCS11Exception {
		try {
			myPKCS11Module_.C_Login(session_, PKCS11Constants.CKU_USER,
					password.toCharArray());
		} catch (PKCS11Exception e) {
			if (e.getErrorCode() == PKCS11Constants.CKR_USER_ALREADY_LOGGED_IN) {
				logout();
				loginUser(password);
			} else if (e.getErrorCode() == PKCS11Constants.CKR_PIN_INCORRECT) {
				return 1;
			} else {
				return 2;
			}
		}
		Log.e(Constant.Debug, "Login OK");
		return 0;
	}
	
	public static int unblockPIN(String SOPIN, String NewPin) throws PKCS11Exception
	{
		try {
			myPKCS11Module_.C_Login(session_, PKCS11Constants.CKU_SO, SOPIN.toCharArray());
			myPKCS11Module_.C_InitPIN(session_, NewPin.toCharArray());
			logout();
			return 0;
		} catch (PKCS11Exception e) {
			e.printStackTrace();
			if(e.getErrorCode() == PKCS11Constants.CKR_USER_ALREADY_LOGGED_IN) {
				logout();
				unblockPIN(SOPIN, NewPin);
			} else if(e.getErrorCode() == PKCS11Constants.CKR_PIN_INCORRECT) {
				return 1;
			}
		}
		return -1;
	}
	
	public static long changePassword(String oldPassword, String newPassword) throws PKCS11Exception {
		try {
			if (null == oldPassword || null == newPassword)
				return -1;
			
			myPKCS11Module_.C_SetPIN(session_, oldPassword.toCharArray(), newPassword.toCharArray());
		} catch (PKCS11Exception e) {
			e.printStackTrace();
			return e.getErrorCode();
		}
		return 0;
	}
	
	public static long[] findKeypair(int bitSize) throws PKCS11Exception {
		
		CK_ATTRIBUTE[] findPubKeyAttrList = new CK_ATTRIBUTE[2];
		findPubKeyAttrList[0] = new CK_ATTRIBUTE();
		findPubKeyAttrList[0].type = PKCS11Constants.CKA_CLASS;
		findPubKeyAttrList[0].pValue = new Long(PKCS11Constants.CKO_PUBLIC_KEY);
		findPubKeyAttrList[1] = new CK_ATTRIBUTE();
		findPubKeyAttrList[1].type = PKCS11Constants.CKA_MODULUS_BITS;
		findPubKeyAttrList[1].pValue = new Long(bitSize);
		
		myPKCS11Module_.C_FindObjectsInit(session_, findPubKeyAttrList);
		long[] pubKeyHandle = myPKCS11Module_.C_FindObjects(session_, 1);
		myPKCS11Module_.C_FindObjectsFinal(session_);
		
		if(pubKeyHandle == null)
		{
			return null;
		}
		
		int num = pubKeyHandle.length;
		Log.e(Constant.Debug, "found "+num+" publickey");
		if(num <= 0)
		{
			return null;
		}
		
		long publicKeyHandle_ = pubKeyHandle[0];
		
		CK_ATTRIBUTE[] getModulusAttrList = new CK_ATTRIBUTE[1];
		getModulusAttrList[0] = new CK_ATTRIBUTE();
		getModulusAttrList[0].type = PKCS11Constants.CKA_MODULUS;
		getModulusAttrList[0].pValue = new byte[256];
		
		myPKCS11Module_.C_GetAttributeValue(session_, publicKeyHandle_, getModulusAttrList);
		
		CK_ATTRIBUTE[] priAttrTmplList = new CK_ATTRIBUTE[3];
		
		priAttrTmplList[0] = new CK_ATTRIBUTE();
		priAttrTmplList[0].type = PKCS11Constants.CKA_CLASS;
		priAttrTmplList[0].pValue = new Long(PKCS11Constants.CKO_PRIVATE_KEY);
		
		priAttrTmplList[1] = new CK_ATTRIBUTE();
		priAttrTmplList[1].type = PKCS11Constants.CKA_TOKEN;
		priAttrTmplList[1].pValue = new Boolean(PKCS11Constants.TRUE);
		
		priAttrTmplList[2] = new CK_ATTRIBUTE();
		priAttrTmplList[2].type = PKCS11Constants.CKA_MODULUS;
		priAttrTmplList[2].pValue = getModulusAttrList[0].pValue;
		
		myPKCS11Module_.C_FindObjectsInit(session_, priAttrTmplList);
		long[] priKeyHandle = myPKCS11Module_.C_FindObjects(session_, 1);
		myPKCS11Module_.C_FindObjectsFinal(session_);
		
		Log.e(Constant.Debug, "found "+priKeyHandle.length+" privatekey");
		if(priKeyHandle == null	|| priKeyHandle.length == 0)
		{
			return null;
		}
		
		long[] keyPair = new long[2];
		keyPair[0] = pubKeyHandle[0];
		keyPair[1] = priKeyHandle[0];
		
		return keyPair;
	}
	
	public static long getHandlePubKey(String ID) throws PKCS11Exception
	{
		CK_ATTRIBUTE[] findPubKeyAttrList = new CK_ATTRIBUTE[1];
		findPubKeyAttrList[0] = new CK_ATTRIBUTE();
		findPubKeyAttrList[0].type = PKCS11Constants.CKA_CLASS;
		findPubKeyAttrList[0].pValue = new Long(PKCS11Constants.CKO_PUBLIC_KEY);
		
		myPKCS11Module_.C_FindObjectsInit(session_, findPubKeyAttrList);
		long[] pubKeyHandle = myPKCS11Module_.C_FindObjects(session_, MAX_OBJS);
		myPKCS11Module_.C_FindObjectsFinal(session_);
		
		if(pubKeyHandle == null)
		{
			return 0;
		}
		
		int num = pubKeyHandle.length;
		Log.e(Constant.Debug, "found "+num+" publickey");
		
		for(int i=0; i<num; i++)
		{
			byte[] certBytes = null;
			CK_ATTRIBUTE[] template = new CK_ATTRIBUTE[1];
			template[0] = new CK_ATTRIBUTE();
			template[0].type = PKCS11Constants.CKA_ID;
			
			myPKCS11Module_.C_GetAttributeValue(session_, pubKeyHandle[i], template);
			certBytes = (byte[]) template[0].pValue;
			String priID = bytesToHex(certBytes);
			Log.d(Constant.Debug, "PubID: "+priID);
			if(priID.compareTo(ID)==0)
				return pubKeyHandle[i];
		}
		return 0;
	}
	
	public static long getHandlePriKey(String ID) throws PKCS11Exception, UnsupportedEncodingException
	{
		CK_ATTRIBUTE[] findPriKeyAttrList = new CK_ATTRIBUTE[1];
		findPriKeyAttrList[0] = new CK_ATTRIBUTE();
		findPriKeyAttrList[0].type = PKCS11Constants.CKA_CLASS;
		findPriKeyAttrList[0].pValue = new Long(PKCS11Constants.CKO_PRIVATE_KEY);
		
		myPKCS11Module_.C_FindObjectsInit(session_, findPriKeyAttrList);
		long[] priKeyHandle = myPKCS11Module_.C_FindObjects(session_, MAX_OBJS);
		myPKCS11Module_.C_FindObjectsFinal(session_);
		
		if(priKeyHandle == null)
		{
			return 0;
		}
		
		int num = priKeyHandle.length;
		Log.e(Constant.Debug, "found "+num+" privatekey");
		
		for(int i=0; i<num; i++)
		{
			byte[] certBytes = null;
			CK_ATTRIBUTE[] template = new CK_ATTRIBUTE[1];
			template[0] = new CK_ATTRIBUTE();
			template[0].type = PKCS11Constants.CKA_ID;
			
			myPKCS11Module_.C_GetAttributeValue(session_, priKeyHandle[i], template);
			certBytes = (byte[]) template[0].pValue;
			String priID = bytesToHex(certBytes);
			Log.d(Constant.Debug, "PriID: "+priID);
			if(priID.compareTo(ID)==0)
				return priKeyHandle[i];
		}
		return 0;
	}
	public static byte[] getCertificateValue(String ID) throws PKCS11Exception
	{
		CK_ATTRIBUTE[] findCerAttrList = new CK_ATTRIBUTE[1];
		findCerAttrList[0] = new CK_ATTRIBUTE();
		findCerAttrList[0].type = PKCS11Constants.CKA_CLASS;
		findCerAttrList[0].pValue = new Long(PKCS11Constants.CKO_CERTIFICATE);
		
		myPKCS11Module_.C_FindObjectsInit(session_, findCerAttrList);
		long[] CerHandle = myPKCS11Module_.C_FindObjects(session_, MAX_OBJS);
		myPKCS11Module_.C_FindObjectsFinal(session_);
		
		if(CerHandle == null)
		{
			return null;
		}
		
		int num = CerHandle.length;
		if(num <= 0)
		{
			System.out.println("found: 0 certificate");
			return null;
		} else
		{
			System.out.println("found: "+num+" certificates");
		}

		byte[] cerID = null;
		byte[] cerValue = null;
		
		CK_ATTRIBUTE[] getCKAValueAttrList = new CK_ATTRIBUTE[2];
		getCKAValueAttrList[0] = new CK_ATTRIBUTE();
		getCKAValueAttrList[0].type = PKCS11Constants.CKA_ID;
		getCKAValueAttrList[1] = new CK_ATTRIBUTE();
		getCKAValueAttrList[1].type = PKCS11Constants.CKA_VALUE;
		
		CertItem[] result = new CertItem[CerHandle.length];
		for(int i=0; i<CerHandle.length; i++)
		{
			myPKCS11Module_.C_GetAttributeValue(session_, CerHandle[i], getCKAValueAttrList);
			cerID = (byte[]) getCKAValueAttrList[0].pValue;
			cerValue = (byte[]) getCKAValueAttrList[1].pValue;
			String id = bytesToHex(cerID);
			if(id.compareTo(ID) == 0)
				return cerValue;
		}
		return null;
	}
	
	public static long[] generateKeypair(int bitSize) throws PKCS11Exception {

		CK_ATTRIBUTE[] pukAttrTmplList = new CK_ATTRIBUTE[7];
		pukAttrTmplList[0] = new CK_ATTRIBUTE();
		pukAttrTmplList[0].type = PKCS11Constants.CKA_CLASS;
		pukAttrTmplList[0].pValue = new Long(PKCS11Constants.CKO_PUBLIC_KEY);
		pukAttrTmplList[1] = new CK_ATTRIBUTE();
		pukAttrTmplList[1].type = PKCS11Constants.CKA_KEY_TYPE;
		pukAttrTmplList[1].pValue = new Long(PKCS11Constants.CKK_RSA);
		pukAttrTmplList[2] = new CK_ATTRIBUTE();
		pukAttrTmplList[2].type = PKCS11Constants.CKA_SUBJECT;
		byte[] subject = "Sample RSA Key Pair".getBytes();
		pukAttrTmplList[2].pValue = subject;
		pukAttrTmplList[3] = new CK_ATTRIBUTE();
		pukAttrTmplList[3].type = PKCS11Constants.CKA_MODULUS_BITS;
		pukAttrTmplList[3].pValue = new Long(bitSize);
		pukAttrTmplList[4] = new CK_ATTRIBUTE();
		pukAttrTmplList[4].type = PKCS11Constants.CKA_ENCRYPT;
		pukAttrTmplList[4].pValue = new Boolean(PKCS11Constants.TRUE);
		pukAttrTmplList[5] = new CK_ATTRIBUTE();
		pukAttrTmplList[5].type = PKCS11Constants.CKA_TOKEN;
		pukAttrTmplList[5].pValue = new Boolean(PKCS11Constants.TRUE);
		pukAttrTmplList[6] = new CK_ATTRIBUTE();
		pukAttrTmplList[6].type = PKCS11Constants.CKA_WRAP;
		pukAttrTmplList[6].pValue = new Boolean(PKCS11Constants.TRUE);

		CK_ATTRIBUTE[] prkAttrTmplList = new CK_ATTRIBUTE[9];
		prkAttrTmplList[0] = new CK_ATTRIBUTE();
		prkAttrTmplList[0].type = PKCS11Constants.CKA_CLASS;
		prkAttrTmplList[0].pValue = new Long(PKCS11Constants.CKO_PRIVATE_KEY);
		prkAttrTmplList[1] = new CK_ATTRIBUTE();
		prkAttrTmplList[1].type = PKCS11Constants.CKA_KEY_TYPE;
		prkAttrTmplList[1].pValue = new Long(PKCS11Constants.CKK_RSA);
		prkAttrTmplList[2] = new CK_ATTRIBUTE();
		prkAttrTmplList[2].type = PKCS11Constants.CKA_SUBJECT;
		prkAttrTmplList[2].pValue = subject;
		prkAttrTmplList[3] = new CK_ATTRIBUTE();
		prkAttrTmplList[3].type = PKCS11Constants.CKA_DECRYPT;
		prkAttrTmplList[3].pValue = new Boolean(PKCS11Constants.TRUE);
		prkAttrTmplList[4] = new CK_ATTRIBUTE();
		prkAttrTmplList[4].type = PKCS11Constants.CKA_PRIVATE;
		prkAttrTmplList[4].pValue = new Boolean(PKCS11Constants.TRUE);
		prkAttrTmplList[5] = new CK_ATTRIBUTE();
		prkAttrTmplList[5].type = PKCS11Constants.CKA_SENSITIVE;
		prkAttrTmplList[5].pValue = new Boolean(PKCS11Constants.TRUE);
		prkAttrTmplList[6] = new CK_ATTRIBUTE();
		prkAttrTmplList[6].type = PKCS11Constants.CKA_TOKEN;
		prkAttrTmplList[6].pValue = new Boolean(PKCS11Constants.TRUE);
		prkAttrTmplList[7] = new CK_ATTRIBUTE();
		prkAttrTmplList[7].type = PKCS11Constants.CKA_EXTRACTABLE;
		prkAttrTmplList[7].pValue = new Boolean(PKCS11Constants.TRUE);
		prkAttrTmplList[8] = new CK_ATTRIBUTE();
		prkAttrTmplList[8].type = PKCS11Constants.CKA_UNWRAP;
		prkAttrTmplList[8].pValue = new Boolean(PKCS11Constants.TRUE);

		CK_MECHANISM genkeyMechanism_ = new CK_MECHANISM();
		genkeyMechanism_.mechanism = PKCS11Constants.CKM_RSA_PKCS_KEY_PAIR_GEN;
		genkeyMechanism_.pParameter = null;
		long[] objectHandles = myPKCS11Module_.C_GenerateKeyPair(session_,
				genkeyMechanism_, pukAttrTmplList, prkAttrTmplList);

		return objectHandles;
	}
	
	public static byte[] signData(byte[] signData, long privateKeyHandle_, long mechanism) throws PKCS11Exception {

		if (null == signData)
			return null;
		
		CK_MECHANISM signatureMechanism_ = new CK_MECHANISM();
		signatureMechanism_.mechanism = mechanism;
		signatureMechanism_.pParameter = null;

		myPKCS11Module_.C_SignInit(session_, signatureMechanism_,
				privateKeyHandle_);
		
		byte[] signature_ = myPKCS11Module_.C_Sign(session_, signData);
		
		return signature_;
	}

	public static void verifySignature(byte[] signData, byte[] signature_, long publicKeyHandle_, long mechanism) throws PKCS11Exception {

		if (null == signData || null == signature_)
			return;
		
		
		CK_MECHANISM signatureMechanism_ = new CK_MECHANISM();
		signatureMechanism_.mechanism = mechanism;
		signatureMechanism_.pParameter = null;
		try {
			myPKCS11Module_.C_VerifyInit(session_, signatureMechanism_,
					publicKeyHandle_);
			myPKCS11Module_.C_Verify(session_, signData, signature_);
		} catch (PKCS11Exception e) {
			if (e.getErrorCode() != PKCS11Constants.CKR_OK) {
				
			}
			e.printStackTrace();
			throw e;
		}

	}
	
	public static byte[] encryptData(String encryptdata, long publicKeyHandle_) throws PKCS11Exception {

		if (null == encryptdata)
			return null;
		
		byte[] data = encryptdata.getBytes();

		CK_MECHANISM encryptMechanism_ = new CK_MECHANISM();
		encryptMechanism_.mechanism = PKCS11Constants.CKM_RSA_PKCS;
		encryptMechanism_.pParameter = null;
		byte[] cipher_;
		try {
			myPKCS11Module_.C_EncryptInit(session_, encryptMechanism_,
					publicKeyHandle_);
			cipher_ = myPKCS11Module_.C_Encrypt(session_, data);
		} catch (PKCS11Exception e) {
			if (e.getErrorCode() != PKCS11Constants.CKR_OK) {

			}
			e.printStackTrace();
			throw e;
		}

		return cipher_;
	}
	
	public static byte[] decryptData(byte[] cipher_, long privateKeyHandle_) throws PKCS11Exception {

		byte[] plain = null;
		if (null == cipher_)
			return null;
		
		CK_MECHANISM encryptMechanism_ = new CK_MECHANISM();
		encryptMechanism_.mechanism = PKCS11Constants.CKM_RSA_PKCS;
		encryptMechanism_.pParameter = null;
		try {
			myPKCS11Module_.C_DecryptInit(session_, encryptMechanism_,
					privateKeyHandle_);
			plain = myPKCS11Module_.C_Decrypt(session_, cipher_);
		} catch (PKCS11Exception e) {

			e.printStackTrace();
			throw e;
		}

		return plain;
	}
	
	public static byte[] digest(String data, long mechanism) throws PKCS11Exception {
		byte[] digest = null;

		if (null == data)
			return null;
		
		CK_MECHANISM Mechanism_ = new CK_MECHANISM();
		Mechanism_.mechanism = mechanism;
		Mechanism_.pParameter = null;
		try {
			myPKCS11Module_.C_DigestInit(session_, Mechanism_);
			digest = myPKCS11Module_.C_Digest(session_, data.getBytes());
		} catch (PKCS11Exception e) {
			e.printStackTrace();
			throw e;
		}
		return digest;
	}
	
    public static long getSessionHandle()
    {
    	return session_;
    }
    
    public static PKCS11 getPKCS11Module()
    {
    	return myPKCS11Module_;
    }
	
	public static void cleanup(long[] objectHandles) throws PKCS11Exception {

		
		if (null == objectHandles || objectHandles.length < 2)
			return;
		
		try {
			myPKCS11Module_.C_DestroyObject(session_, objectHandles[0]);
			myPKCS11Module_.C_DestroyObject(session_, objectHandles[1]);
		} catch (PKCS11Exception e) {

			e.printStackTrace();
			throw e;
		}

	}

	public static void logout() throws PKCS11Exception {
		myPKCS11Module_.C_Logout(session_);
		Log.e(Constant.Debug, "P11Other - Logout");
	}
	
	public static void p11finalize() {
		try {
			myPKCS11Module_.C_Finalize(null);
			myPKCS11Module_.finalize();
			isLoadlib=0;
			if(audioToken!=null){
				audioToken.Release();
				audioToken=null;
			}
			
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}
	
    public static long[] findCertificates() throws PKCS11Exception
    {
		long certKeyHandle = -1L;
		
		Log.e(Constant.Debug,"finding all certificates on token ...");
		
		CK_ATTRIBUTE[] attributeTemplateList = new CK_ATTRIBUTE[1];
		
		attributeTemplateList[0] = new CK_ATTRIBUTE();
		attributeTemplateList[0].type = PKCS11Constants.CKA_CLASS;
		attributeTemplateList[0].pValue = new Long(PKCS11Constants.CKO_CERTIFICATE);
		
		myPKCS11Module_.C_FindObjectsInit(session_, attributeTemplateList);
		
		long[] availableCertificates = myPKCS11Module_.C_FindObjects(session_, 100);
		//maximum of 100 at once
		myPKCS11Module_.C_FindObjectsFinal(session_);
		
		if (availableCertificates == null) {
			Log.e(Constant.Debug,"null returned - no certificate key found");
		} else {
		    Log.e(Constant.Debug, "found " + availableCertificates.length
		                + " certificates");
		}
		
		return availableCertificates;
	}
    
    public static byte[] getDEREncodedCertificate(long certHandle) throws PKCS11Exception {
	
    	Log.e(Constant.Debug,"reading certificate bytes");
		byte[] certBytes = null;
		
		CK_ATTRIBUTE[] template = new CK_ATTRIBUTE[1];
		template[0] = new CK_ATTRIBUTE();
		template[0].type = PKCS11Constants.CKA_VALUE;
		
		myPKCS11Module_.C_GetAttributeValue(session_, certHandle, template);
		
		certBytes = (byte[]) template[0].pValue;
		
		return certBytes;
	}
    
    public static CertItem[] getAllCerts() throws PKCS11Exception
    {
		CK_ATTRIBUTE[] findCerAttrList = new CK_ATTRIBUTE[1];
		findCerAttrList[0] = new CK_ATTRIBUTE();
		findCerAttrList[0].type = PKCS11Constants.CKA_CLASS;
		findCerAttrList[0].pValue = new Long(PKCS11Constants.CKO_CERTIFICATE);
		
		myPKCS11Module_.C_FindObjectsInit(session_, findCerAttrList);
		long[] CerHandle = myPKCS11Module_.C_FindObjects(session_, MAX_OBJS);
		myPKCS11Module_.C_FindObjectsFinal(session_);
		
		if(CerHandle == null)
		{
			return null;
		}
		
		int num = CerHandle.length;
		if(num <= 0)
		{
			System.out.println("found: 0 certificate");
			return null;
		} else
		{
			System.out.println("found: "+num+" certificates");
		}

		byte[] cerID = null;
		byte[] cerValue = null;
		
		CK_ATTRIBUTE[] getCKAValueAttrList = new CK_ATTRIBUTE[2];
		getCKAValueAttrList[0] = new CK_ATTRIBUTE();
		getCKAValueAttrList[0].type = PKCS11Constants.CKA_ID;
		getCKAValueAttrList[1] = new CK_ATTRIBUTE();
		getCKAValueAttrList[1].type = PKCS11Constants.CKA_VALUE;
		
		CertItem[] result = new CertItem[CerHandle.length];
		for(int i=0; i<CerHandle.length; i++)
		{
			myPKCS11Module_.C_GetAttributeValue(session_, CerHandle[i], getCKAValueAttrList);
			cerID = (byte[]) getCKAValueAttrList[0].pValue;
			cerValue = (byte[]) getCKAValueAttrList[1].pValue;
			result[i] = new CertItem(getCN(getDNFromX509(cerValue)), bytesToHex(cerID), cerValue);
			Log.i(Constant.Debug, "Cert "+i+": "+bytesToHex(cerID));
		}
		return result;    	
    }
    
    private static String getCN(String DN)
    {
    	String[] split = DN.split(","); 
    	for (String x : split) {
    	    if (x.contains("CN=")) {
    	        return x.trim().substring(3);
    	    }
    	}
    	return "";
    }
    
    private static String getDNFromX509(byte[] cerValue)
    {
		InputStream inStream = new ByteArrayInputStream(cerValue);
		CertificateFactory cf;
		X509Certificate x509cer = null;
		try {
			cf = CertificateFactory.getInstance("X.509");
			x509cer = (X509Certificate) cf.generateCertificate(inStream);
		} catch (java.security.cert.CertificateException e) {
			e.printStackTrace();
		}
		
		return x509cer.getSubjectDN().toString();
    }
    
    public static CK_TOKEN_INFO getTokenInformation()
    {
    	return tokenInfo;
    }
    
    public static int importCertificate(byte[] certByte)
    {
    	try {
			Security.addProvider(new IAIK());
			try {
				Class eccProviderClass = Class.forName("iaik.security.ecc.provider.ECCProvider");
				Provider eccProvider = (Provider) eccProviderClass.newInstance();
				Security.addProvider(eccProvider);
			} catch (Exception ex) {
				// ignore, we only need it for pkcs#12 files containing ECC keys
				ex.printStackTrace();
			}
			
			//  parse certificate
			CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "IAIK");
			
			byte[] raw_cert = certByte;
			InputStream fileInputStream = new ByteArrayInputStream(raw_cert);
			
			//FileInputStream fileInputStream = new FileInputStream(certData);
			Collection certificateChain = certificateFactory.generateCertificates(fileInputStream);
			if (certificateChain.size() < 1) {
				System.out.println("Did not find any certificate in the given input file. Finished.");
				//output_.flush();
				throw new CertificateException("No certificate found!");
			}
			iaik.x509.X509Certificate x509Certificate = (iaik.x509.X509Certificate) certificateChain
				    .iterator().next();
				certificateChain.remove(x509Certificate);
				System.out.println("Searching for corresponding private key on token.");
	
				PublicKey publicKey = x509Certificate.getPublicKey();
				
				Object searchTemplate = null;
				if (publicKey.getAlgorithm().equalsIgnoreCase("RSA")) {
					java.security.interfaces.RSAPublicKey rsaPublicKey = (java.security.interfaces.RSAPublicKey) publicKey;
					RSAPrivateKey rsaPrivateKeySearchTemplate = new RSAPrivateKey();
					byte[] modulus = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(rsaPublicKey.getModulus());
					rsaPrivateKeySearchTemplate.getModulus().setByteArrayValue(modulus);
					searchTemplate = rsaPrivateKeySearchTemplate;
				} else if (publicKey.getAlgorithm().equalsIgnoreCase("DSA")) {
					java.security.interfaces.DSAPublicKey dsaPublicKey = (java.security.interfaces.DSAPublicKey) publicKey;
					DSAParams dsaParams = dsaPublicKey.getParams();
					DSAPrivateKey dsaPrivateKeySearchTemplate = new DSAPrivateKey();
					byte[] g = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(dsaParams.getG());
					byte[] p = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(dsaParams.getP());
					byte[] q = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(dsaParams.getQ());
					dsaPrivateKeySearchTemplate.getBase().setByteArrayValue(g);
					dsaPrivateKeySearchTemplate.getPrime().setByteArrayValue(p);
					dsaPrivateKeySearchTemplate.getSubprime().setByteArrayValue(q);
					searchTemplate = dsaPrivateKeySearchTemplate;
				} else if (publicKey.getAlgorithm().equalsIgnoreCase("DH")
				    || publicKey.getAlgorithm().equalsIgnoreCase("DiffieHellman")) {
					javax.crypto.interfaces.DHPublicKey dhPublicKey = (javax.crypto.interfaces.DHPublicKey) publicKey;
					DHParameterSpec dhParams = dhPublicKey.getParams();
					DHPrivateKey dhPrivateKeySearchTemplate = new DHPrivateKey();
					byte[] g = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(dhParams.getG());
					byte[] p = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(dhParams.getP());
					dhPrivateKeySearchTemplate.getBase().setByteArrayValue(g);
					dhPrivateKeySearchTemplate.getPrime().setByteArrayValue(p);
					searchTemplate = dhPrivateKeySearchTemplate;
				}
				
				byte[] objectID = null;
				Object tmp = null;
				if (searchTemplate != null) {
					_session_.findObjectsInit(searchTemplate);
					Object[] foundKeyObjects = _session_.findObjects(1);
					if (foundKeyObjects.length > 0) {
						tmp = foundKeyObjects[0];
						Key foundKey = (Key) foundKeyObjects[0];
						objectID = foundKey.getId().getByteArrayValue();
						System.out.println("found a correponding key on the token: ");
					} else {
						System.out.println("found no correponding key on the token.");
						return 1;
					}
					_session_.findObjectsFinal();
				} else {
					System.out.println("public key is neither RSA, DSA nor DH.");
				}
			System.out.println("Create certificate object(s) on token.");
	
			iaik.x509.X509Certificate currentCertificate = x509Certificate; // start with user cert
			boolean importedCompleteChain = false;
			while (!importedCompleteChain) {
				// create certificate object template
				X509PublicKeyCertificate pkcs11X509PublicKeyCertificate = new X509PublicKeyCertificate();
				Name subjectName = (Name) currentCertificate.getSubjectDN();
				Name issuerName = (Name) currentCertificate.getIssuerDN();
				String subjectCommonName = subjectName.getRDN(ObjectID.commonName);
				String issuerCommonName = issuerName.getRDN(ObjectID.commonName);
				char[] label = (subjectCommonName + "'s "
				    + ((issuerCommonName != null) ? issuerCommonName + " " : "") + "Certificate")
				    .toCharArray();
				byte[] newObjectID;
				// if we need a new object ID, create one
				if (objectID == null) {
					if (publicKey instanceof java.security.interfaces.RSAPublicKey) {
						newObjectID = ((java.security.interfaces.RSAPublicKey) publicKey).getModulus()
						    .toByteArray();
						MessageDigest digest = MessageDigest.getInstance("SHA-1");
						newObjectID = digest.digest(newObjectID);
					} else if (publicKey instanceof java.security.interfaces.DSAPublicKey) {
						newObjectID = ((java.security.interfaces.DSAPublicKey) publicKey).getY()
						    .toByteArray();
						MessageDigest digest = MessageDigest.getInstance("SHA-1");
						newObjectID = digest.digest(newObjectID);
					} else {
						newObjectID = currentCertificate.getFingerprint("SHA-1");
					}
				} else {
					// we already got one from a corresponding private key before
					newObjectID = objectID;
				}
	
				byte[] encodedSubject = ((Name) currentCertificate.getSubjectDN()).getEncoded();
				byte[] encodedIssuer = ((Name) currentCertificate.getIssuerDN()).getEncoded();
	
				// serial number should be an DER encoded ASN.1 integer		 
				// Netscape deviates from the standard here, for use with Netscape rather use
				pkcs11X509PublicKeyCertificate.getSetAttributes(tmp);
				byte[] serialNumber = currentCertificate.getSerialNumber().toByteArray();
	
				pkcs11X509PublicKeyCertificate.getToken().setBooleanValue(Boolean.TRUE);
				pkcs11X509PublicKeyCertificate.getPrivate().setBooleanValue(Boolean.FALSE);
				pkcs11X509PublicKeyCertificate.getLabel().setCharArrayValue(label);
				pkcs11X509PublicKeyCertificate.getId().setByteArrayValue(newObjectID);
				pkcs11X509PublicKeyCertificate.getSubject().setByteArrayValue(encodedSubject);
				pkcs11X509PublicKeyCertificate.getIssuer().setByteArrayValue(encodedIssuer);
				pkcs11X509PublicKeyCertificate.getSerialNumber().setByteArrayValue(serialNumber);
				pkcs11X509PublicKeyCertificate.getValue().setByteArrayValue(currentCertificate.getEncoded());
				System.out.println(pkcs11X509PublicKeyCertificate);
				System.out.println("________________________________________________________________________________");
	
				CK_ATTRIBUTE[] attr = new CK_ATTRIBUTE[11];
				attr[0] = new CK_ATTRIBUTE();
				attr[0].type = PKCS11Constants.CKA_CLASS;
				attr[0].pValue = new Long(PKCS11Constants.CKO_CERTIFICATE);
				
				attr[1] = new CK_ATTRIBUTE();
				attr[1].type = PKCS11Constants.CKA_TOKEN;
				attr[1].pValue = new Boolean(PKCS11Constants.TRUE);
				
				attr[2] = new CK_ATTRIBUTE();
				attr[2].type = PKCS11Constants.CKA_PRIVATE;
				attr[2].pValue = new Boolean(PKCS11Constants.FALSE);
				
				attr[3] = new CK_ATTRIBUTE();
				attr[3].type = PKCS11Constants.CKA_MODIFIABLE;
				attr[3].pValue = new Boolean(PKCS11Constants.TRUE);
				
				attr[4] = new CK_ATTRIBUTE();
				attr[4].type = PKCS11Constants.CKA_LABEL;
				attr[4].pValue = (char[])label;
				
				attr[5] = new CK_ATTRIBUTE();
				attr[5].type = PKCS11Constants.CKA_CERTIFICATE_TYPE;
				attr[5].pValue = new Long(PKCS11Constants.CKC_X_509);
				
				attr[6] = new CK_ATTRIBUTE();
				attr[6].type = PKCS11Constants.CKA_SUBJECT;
				attr[6].pValue = (byte[])encodedSubject;
				
				attr[7] = new CK_ATTRIBUTE();
				attr[7].type = PKCS11Constants.CKA_ID;
				attr[7].pValue = (byte[])newObjectID;
				
				attr[8] = new CK_ATTRIBUTE();
				attr[8].type = PKCS11Constants.CKA_ISSUER;
				attr[8].pValue = (byte[])encodedIssuer;
				
				attr[9] = new CK_ATTRIBUTE();
				attr[9].type = PKCS11Constants.CKA_SERIAL_NUMBER;
				attr[9].pValue = (byte[])serialNumber;
				
				attr[10] = new CK_ATTRIBUTE();
				attr[10].type = PKCS11Constants.CKA_VALUE;
				attr[10].pValue = (byte[])currentCertificate.getEncoded();
				
				/*
				String ContainerName = "91C899E1-B3CA-4EF3-A683-B8C04E26D701";
				attr[11] = new CK_ATTRIBUTE();
				attr[11].type = PKCS11Constants.CKA_CONTAINER_NAME;
				attr[11].pValue = (char[])ContainerName.toCharArray();
				*/
				deleteCertificateByID(P11Other.bytesToHex(newObjectID));
				
				myPKCS11Module_.C_CreateObject(session_, attr);
	
				if (certificateChain.size() > 0) {
					currentCertificate = (iaik.x509.X509Certificate) certificateChain.iterator()
					    .next();
					certificateChain.remove(currentCertificate);
					objectID = null; // do not use the same ID for other certificates
				} else {
					importedCompleteChain = true;
				}
				return 0;
			}
    	} catch (Exception e) {
			// TODO: handle exception
    		e.printStackTrace();
		}
    	return 1;
    }
    
	public static String getContainerByID(String KeyID)
	{
		try {
			
			CK_ATTRIBUTE[] findCerAttrList = new CK_ATTRIBUTE[1];
			findCerAttrList[0] = new CK_ATTRIBUTE();
			findCerAttrList[0].type = PKCS11Constants.CKA_CLASS;
			findCerAttrList[0].pValue = new Long(PKCS11Constants.CKO_PUBLIC_KEY);
			
			myPKCS11Module_.C_FindObjectsInit(session_, findCerAttrList);
			long[] CerHandle = myPKCS11Module_.C_FindObjects(session_, MAX_OBJS);
			myPKCS11Module_.C_FindObjectsFinal(session_);
			
			if(CerHandle == null)
			{
				return null;
			}
			
			int num = CerHandle.length;
			if(num <= 0)
			{
				System.out.println("found: 0 publickey");
				return null;
			} else
			{
				System.out.println("found: "+num+" publickey");
			}
			byte[] containerName = null;
			String ID = null;
			for(int i=0; i<num; i++)
			{
				CK_ATTRIBUTE[] getCKAValueAttrList = new CK_ATTRIBUTE[1];
				getCKAValueAttrList[0] = new CK_ATTRIBUTE();
				getCKAValueAttrList[0].type = PKCS11Constants.CKA_CONTAINER_NAME;
				myPKCS11Module_.C_GetAttributeValue(session_, CerHandle[i], getCKAValueAttrList);
				containerName = (byte[])getCKAValueAttrList[0].pValue;
				ID = new String(containerName);
				Log.i(Constant.Debug, ID);
			}
			return ID;
		} catch(PKCS11Exception e)
		{
			e.printStackTrace();
		}
		return null;
	}
    	
	public static String bytesToHex(byte[] bytes) {
	    char[] hexChars = new char[bytes.length * 2];
	    int v;
	    for ( int j = 0; j < bytes.length; j++ ) {
	        v = bytes[j] & 0xFF;
	        hexChars[j * 2] = hexArray[v >>> 4];
	        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
	    }
	    return new String(hexChars);
	}
	
	public static X509Certificate getCerFromByte(byte[] certValue)
	{
		X509Certificate x509 = null;
		
		InputStream inStream = new ByteArrayInputStream(certValue);
		CertificateFactory cf;
		try {
			cf = CertificateFactory.getInstance("X.509");
			x509 = (X509Certificate) cf.generateCertificate(inStream);
		} catch (java.security.cert.CertificateException e) {
			e.printStackTrace();
		}
		return x509;
	}
	
	private static void deleteCertificateByID(String CertID) throws PKCS11Exception
	{
		CK_ATTRIBUTE[] findCerAttrList = new CK_ATTRIBUTE[1];
		findCerAttrList[0] = new CK_ATTRIBUTE();
		findCerAttrList[0].type = PKCS11Constants.CKA_CLASS;
		findCerAttrList[0].pValue = new Long(PKCS11Constants.CKO_CERTIFICATE);
		
		myPKCS11Module_.C_FindObjectsInit(session_, findCerAttrList);
		long[] CerHandle = myPKCS11Module_.C_FindObjects(session_, MAX_OBJS);
		myPKCS11Module_.C_FindObjectsFinal(session_);
		
		if(CerHandle == null)
		{
			System.out.println("Cert not found");
		}
		
		int num = CerHandle.length;
		if(num <= 0)
		{
			System.out.println("found: 0 certificate");
		}
		else
		{
			System.out.println("found: "+num+" certificates");
			
			for(int i=0; i< CerHandle.length; i++)
			{
				byte[] cerID = null;
				CK_ATTRIBUTE[] getCKAValueAttrList = new CK_ATTRIBUTE[1];

				getCKAValueAttrList[0] = new CK_ATTRIBUTE();
				getCKAValueAttrList[0].type = PKCS11Constants.CKA_ID;
				
				myPKCS11Module_.C_GetAttributeValue(session_, CerHandle[i], getCKAValueAttrList);

				cerID = (byte[]) getCKAValueAttrList[0].pValue;
				if(CertID.compareTo(bytesToHex(cerID))==0)
				{
					System.out.println("Delete cert: "+bytesToHex(cerID));
					System.out.println("Handle cert: "+CerHandle[i]);
					myPKCS11Module_.C_DestroyObject(session_, CerHandle[i]);
					break;
				}
			}
		}

	}	
	
	//Advanced functions
	public static boolean connectToken() {
		try {
			_module_ = Module.getInstance("/data/data/com.tomicalab.mobiletoken.ckca/lib/libtomikey-2003a.so"
					, "/data/data/com.tomicalab.mobiletoken.ckca/lib/libtomikey-2003a.so");
			_slots_ = _module_.getSlotList(Module.SlotRequirement.TOKEN_PRESENT);
			_token_ = _slots_[0].getToken();
			_session_ = _token_.openSession(Token.SessionType.SERIAL_SESSION,
					Token.SessionReadWriteBehavior.RW_SESSION, null, null);
			return true;
		} catch (TokenException e) {
			e.printStackTrace();
			Log.e(Constant.Debug, "[Advanced functions] ConnectToken failed");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Log.e(Constant.Debug, "[Advanced functions] ConnectToken OK");
		return false;
	}
	
}
